/* Copyright (c) 2003-2007 Caleb Doise
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace SharpScraps.Win32.UI
{

    public partial class WindowDragSelectorControl : UserControl
    {
        private bool bIsDragging = false;
        private Cursor oldCursor = null;

        private IntPtr selectedWindow = IntPtr.Zero;

        private bool bTopLevelWindowsOnly = false;


        public WindowDragSelectorControl()
        {
            InitializeComponent();

            this.Paint += new PaintEventHandler(WindowDragSelectorControl_Paint);
            this.MouseDown += new MouseEventHandler(WindowDragSelectorControl_MouseDown);
        }

        

        public IntPtr SelectedWindow { get { return this.selectedWindow; } }

        public event EventHandler SelectedWindowChanged;

        private void FireSelectedWindowChanged()
        {
            EventHandler eh = this.SelectedWindowChanged;
            if (eh != null)
                eh(this, null);
        }

        public event EventHandler WindowSelected;

        private void FireWindowSelected()
        {
            EventHandler eh = this.WindowSelected;
            if (eh != null)
                eh(this, null);
        }


        public bool TopLevelWindowsOnly
        {
            get { return this.bTopLevelWindowsOnly; }
            set { this.bTopLevelWindowsOnly = value; }
        }


        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case Win32Messages.WM_LBUTTONUP: this.ReleaseMouse(); break;
                case Win32Messages.WM_MOUSEMOVE: this.ProcessMouseMove(); break;
            };

            base.WndProc(ref m);
        }


        void WindowDragSelectorControl_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
                this.CaptureMouse();
        }

        private void CaptureMouse()
        {
            if (!this.bIsDragging)
            {
                this.bIsDragging = true;

                USER32.SetCapture(this.Handle);

                this.oldCursor = this.Cursor;
                this.Cursor = Cursors.Cross;
            }
        }

        private void ReleaseMouse()
        {
            if (this.bIsDragging)
            {
                this.bIsDragging = false;

                USER32.ReleaseCapture();

                this.Cursor = this.oldCursor;
                this.oldCursor = null;

                if (this.selectedWindow != IntPtr.Zero)
                    WindowHelper.RefreshWindow(this.selectedWindow);

                this.FireWindowSelected();
            }
        }


        private IntPtr GetWindowFromPoint(POINT pos)
        {
            IntPtr hWnd = USER32.WindowFromPoint(pos);

            if (this.bTopLevelWindowsOnly)
            {
                hWnd = WindowHelper.GetTopLevelParentWindow(hWnd);
            }

            return hWnd;
        }


        private void ProcessMouseMove()
        {
            if (this.bIsDragging)
            {
                IntPtr hWnd = GetWindowFromPoint(new POINT(Cursor.Position));

                if (hWnd != this.selectedWindow)
                {
                    if (hWnd != IntPtr.Zero)
                        WindowHelper.RefreshWindow(this.selectedWindow);

                    this.selectedWindow = hWnd;
                    this.FireSelectedWindowChanged();

                    this.OutlineWindow(selectedWindow);
                }
            }
        }


        private void OutlineWindow(IntPtr hWnd)
        {
            RECT r;
            USER32.GetWindowRect(hWnd, out r);

            int penWidth = 4;

            IntPtr hDC = USER32.GetWindowDC(hWnd);
            if (hDC != IntPtr.Zero)
            {
                using (Pen pen = new Pen(Color.Black, penWidth))
                using (Graphics g = Graphics.FromHdc(hDC))
                {
                    g.DrawRectangle(pen, penWidth / 2, penWidth / 2, r.Width - penWidth, r.Height - penWidth);
                }
            }

            USER32.ReleaseDC(hWnd, hDC);
        }


        void WindowDragSelectorControl_Paint(object sender, PaintEventArgs e)
        {
            // Draw cross hairs.
            Graphics g = e.Graphics;
            
            Size clientSize = this.ClientSize;

            Point center = new Point(clientSize);
            center.X /= 2;
            center.Y /= 2;

            int length = Math.Min(clientSize.Width, clientSize.Height) * 8 / 10;

            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            using (Pen pen = new Pen(Color.Black, 1))
            {
                g.DrawRectangle(pen, 0, 0, clientSize.Width - 1, clientSize.Height - 1);

                g.DrawLine(pen, center.X, center.Y - length / 2, center.X, center.Y + length / 2);
                g.DrawLine(pen, center.X - length / 2, center.Y, center.X + length / 2, center.Y);
                g.DrawEllipse(pen, center.X - length / 4 - 1.5f, center.Y - length / 4 - 1.5f, length * 2 / 4 + 2.5f, length * 2 / 4 + 2.5f);
            }            
        }
    }
}
